/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "unit_ecma_test.h"
#include "optimizer/ir/datatype.h"
#include "optimizer/ir/graph_cloner.h"
#include "optimizer/optimizations/cleanup.h"
#include "optimizer/optimizations/peepholes.h"

namespace ark::compiler {
class PeepholesTest : public AsmTest {
public:
    PeepholesTest() = default;
};

// NOLINTBEGIN(readability-magic-numbers)
TEST_F(PeepholesTest, CastTrueSimple)
{
    auto graph = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).s32();

        BASIC_BLOCK(2, -1)
        {
            INST(2, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::Add).s32().Inputs(1, 2);
            INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(3);

            INST(5, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(4);
            INST(6, Opcode::Add).s32().Inputs(1, 5);
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(6);

            INST(8, Opcode::Return).any().Inputs(7);
        }
    }

    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).s32();

        BASIC_BLOCK(2, -1)
        {
            INST(2, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::Add).s32().Inputs(1, 2);

            INST(6, Opcode::Add).s32().Inputs(1, 3);
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(6);

            INST(8, Opcode::Return).any().Inputs(7);
        }
    }

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CastTrueComplex)
{
    auto graph = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).s32();

        BASIC_BLOCK(2, 3, 4)
        {
            INST(2, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::Add).s32().Inputs(1, 2);
            INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(3);
            INST(13, Opcode::SaveState).NoVregs();
            INST(5, Opcode::CallDynamic).any().InputsAutoType(4, 13);
            INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(5);
            INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(6);
        }

        BASIC_BLOCK(3, -1)
        {
            INST(8, Opcode::Return).any().Inputs(0);
        }

        BASIC_BLOCK(4, -1)
        {
            INST(9, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(4);
            INST(10, Opcode::Add).s32().Inputs(1, 9);
            INST(11, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(10);

            INST(12, Opcode::Return).any().Inputs(11);
        }
    }

    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).s32();

        BASIC_BLOCK(2, 3, 4)
        {
            INST(2, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::Add).s32().Inputs(1, 2);
            INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(3);
            INST(13, Opcode::SaveState).NoVregs();
            INST(5, Opcode::CallDynamic).any().InputsAutoType(4, 13);
            INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(5);
            INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(6);
        }

        BASIC_BLOCK(3, -1)
        {
            INST(8, Opcode::Return).any().Inputs(0);
        }

        BASIC_BLOCK(4, -1)
        {
            INST(10, Opcode::Add).s32().Inputs(1, 3);
            INST(11, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(10);

            INST(12, Opcode::Return).any().Inputs(11);
        }
    }

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CastFalse)
{
    auto graph = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).s32();

        BASIC_BLOCK(2, -1)
        {
            INST(2, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::Add).s32().Inputs(1, 2);
            INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(3);
            INST(5, Opcode::Return).any().Inputs(4);
        }
    }

    auto graphOpt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph();

    ASSERT_FALSE(graph->RunPass<Peepholes>());
    ASSERT_FALSE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CastIntToAnyToDouble)
{
    auto graph = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).s32();

        BASIC_BLOCK(2, -1)
        {
            INST(1, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(5, Opcode::SaveState).Inputs(1).SrcVregs({0});
            INST(2, Opcode::AnyTypeCheck)
                .any()
                .AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE)
                .Inputs(1, 5)
                .IntegerWasSeen(true);
            INST(3, Opcode::CastAnyTypeValue)
                .f64()
                .AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE)
                .Inputs(2)
                .IntegerWasSeen(true);
            INST(4, Opcode::Return).f64().Inputs(3);
        }
    }

    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        PARAMETER(0, 0).s32();

        BASIC_BLOCK(2, -1)
        {
            INST(1, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(5, Opcode::SaveState).Inputs(0).SrcVregs({0});
            INST(2, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(1, 5);
            INST(6, Opcode::Cast).f64().SrcType(DataType::INT32).Inputs(0);
            INST(4, Opcode::Return).f64().Inputs(6);
        }
    }

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CastBoolToAnyToInt)
{
    auto graph = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).b();

        BASIC_BLOCK(2, -1)
        {
            INST(1, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0);
            INST(2, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(3, Opcode::Return).s32().Inputs(2);
        }
    }

    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        PARAMETER(0, 0).b();

        BASIC_BLOCK(2, -1)
        {
            INST(4, Opcode::Cast).s32().SrcType(DataType::BOOL).Inputs(0);
            INST(3, Opcode::Return).s32().Inputs(4);
        }
    }

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CastAnyToValueAndBack)
{
    auto graph = CreateGraphDynStubWithDefaultRuntime();
    graph->SetUnrollComplete();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();

        BASIC_BLOCK(2, -1)
        {
            INST(1, Opcode::CastAnyTypeValue).ref().AnyType(AnyBaseType::ECMASCRIPT_STRING_TYPE).Inputs(0);
            INST(2, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_STRING_TYPE).Inputs(1);
            INST(3, Opcode::Return).any().Inputs(2);
        }
    }

    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        PARAMETER(0, 0).any();

        BASIC_BLOCK(2, -1)
        {
            INST(3, Opcode::Return).any().Inputs(0);
        }
    }

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CastAnyToValueAndBackNotApplied)
{
    auto graph = CreateGraphDynStubWithDefaultRuntime();
    graph->SetUnrollComplete();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();

        BASIC_BLOCK(2, -1)
        {
            INST(1, Opcode::CastAnyTypeValue)
                .f64()
                .AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE)
                .AllowedInputType(profiling::AnyInputType::INTEGER)
                .Inputs(0);
            INST(2, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(1);
            INST(3, Opcode::Return).any().Inputs(2);
        }
    }

    auto graphOpt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph();
    ASSERT_FALSE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CompareCastToAnyAndCastToAny)
{
    auto graph = CreateGraphDynWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();

        BASIC_BLOCK(2, -1)
        {
            INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32);
            INST(5, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(4);
            INST(6, Opcode::Compare).b().CC(CC_GE).Inputs(2, 3).SrcType(DataType::INT32);
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(6);
            INST(8, Opcode::Compare).b().CC(CC_EQ).Inputs(5, 7).SrcType(DataType::ANY);
            INST(9, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(8);
            INST(10, Opcode::Return).any().Inputs(9);
        }
    }

    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();

        BASIC_BLOCK(2, -1)
        {
            INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32);
            INST(6, Opcode::Compare).b().CC(CC_GE).Inputs(2, 3).SrcType(DataType::INT32);
            INST(8, Opcode::Compare).b().CC(CC_EQ).Inputs(4, 6).SrcType(DataType::BOOL);
            INST(9, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(8);
            INST(10, Opcode::Return).any().Inputs(9);
        }
    }

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CompareCastToAnyAndCastToAnyNotApply)
{
    auto graph = CreateGraphDynWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();

        BASIC_BLOCK(2, -1)
        {
            INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32);
            INST(5, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(4);
            INST(6, Opcode::Add).i32().Inputs(2, 3);
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(6);
            INST(8, Opcode::Compare).b().CC(CC_EQ).Inputs(5, 7).SrcType(DataType::ANY);
            INST(9, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(8);
            INST(10, Opcode::Return).any().Inputs(9);
        }
    }
    auto graphOpt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph();

    ASSERT_FALSE(graph->RunPass<Peepholes>());
    ASSERT_FALSE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CompareCastToAnyAndConstTrue)
{
    auto graph = CreateGraphDynWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();
        CONSTANT(9, TaggedValue::VALUE_TRUE).any();

        BASIC_BLOCK(2, -1)
        {
            INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32);
            INST(5, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(4);
            INST(6, Opcode::Compare).b().CC(CC_EQ).Inputs(5, 9).SrcType(DataType::ANY);
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(6);
            INST(8, Opcode::Return).any().Inputs(7);
        }
    }

    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();

        BASIC_BLOCK(2, -1)
        {
            INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32);
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(4);
            INST(8, Opcode::Return).any().Inputs(7);
        }
    }

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CompareCastToAnyAndConstFalse)
{
    auto graph = CreateGraphDynWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();
        CONSTANT(9, TaggedValue::VALUE_FALSE).any();

        BASIC_BLOCK(2, -1)
        {
            INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32);
            INST(5, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(4);
            INST(6, Opcode::Compare).b().CC(CC_EQ).Inputs(5, 9).SrcType(DataType::ANY);
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(6);
            INST(8, Opcode::Return).any().Inputs(7);
        }
    }

    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();
        CONSTANT(19, 0);

        BASIC_BLOCK(2, -1)
        {
            INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32);
            INST(6, Opcode::Compare).b().CC(CC_EQ).Inputs(4, 19).SrcType(DataType::BOOL);
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(6);
            INST(8, Opcode::Return).any().Inputs(7);
        }
    }

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CompareCastToAnyAndConst)
{
    auto graph = CreateGraphDynWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();
        CONSTANT(9, 10000).any();

        BASIC_BLOCK(2, -1)
        {
            INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32);
            INST(5, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(4);
            INST(6, Opcode::Compare).b().CC(CC_EQ).Inputs(5, 9).SrcType(DataType::ANY);
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(6);
            INST(8, Opcode::Return).any().Inputs(7);
        }
    }

    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        CONSTANT(19, 0);

        BASIC_BLOCK(2, -1)
        {
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(19);
            INST(8, Opcode::Return).any().Inputs(7);
        }
    }

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CompareAnyTypeAfterCastValueToAny)
{
    auto graph = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graph)
    {
        CONSTANT(0, 0);

        BASIC_BLOCK(2, -1)
        {
            INST(2, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);

            INST(3, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE).Inputs(2);
            INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(3);

            INST(5, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_NULL_TYPE).Inputs(2);
            INST(6, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(5);

            INST(7, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(2);
            INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(7);

            INST(9, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(2);
            INST(10, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(9);

            INST(11, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(2);
            INST(12, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(11);

            INST(13, Opcode::SaveState).Inputs(0, 3, 5, 7, 9, 11).SrcVregs({0, 1, 2, 3, 4, 5});
            INST(14, Opcode::CallDynamic).any().InputsAutoType(4, 6, 8, 10, 12, 13);
            INST(15, Opcode::Return).any().Inputs(14);
        }
    }

    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        CONSTANT(0, 0);
        CONSTANT(1, 1);

        BASIC_BLOCK(2, -1)
        {
            INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0);
            INST(6, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0);
            INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0);
            INST(10, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(1);
            INST(12, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0);

            INST(13, Opcode::SaveState).Inputs(0, 0, 0, 0, 1, 0).SrcVregs({0, 1, 2, 3, 4, 5});
            INST(14, Opcode::CallDynamic).any().InputsAutoType(4, 6, 8, 10, 12, 13);
            INST(15, Opcode::Return).any().Inputs(14);
        }
    }

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, AddOverflowCheckXor)
{
    auto graph = CreateGraphDynWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();
        CONSTANT(2, 1);

        BASIC_BLOCK(2, -1)
        {
            INST(3, Opcode::SaveState).Inputs(0, 1, 2).SrcVregs({0, 1, 2});
            INST(4, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(5, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(6, Opcode::AddOverflowCheck).i32().Inputs(4, 5, 3);
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(6);
            INST(8, Opcode::SaveState).Inputs(0, 1, 2, 6).SrcVregs({0, 1, 2, 3});
            INST(9, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(7, 8);
            INST(10, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(9);
            INST(11, Opcode::Xor).i32().Inputs(10, 2);
            INST(12, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(11);
            INST(13, Opcode::Return).any().Inputs(12);
        }
    }
    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();
        CONSTANT(2, 1);

        BASIC_BLOCK(2, -1)
        {
            INST(4, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(5, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(6, Opcode::Add).i32().Inputs(4, 5);
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(6);
            INST(8, Opcode::SaveState).Inputs(0, 1, 2, 6).SrcVregs({0, 1, 2, 3});
            INST(9, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(7, 8);
            INST(11, Opcode::Xor).i32().Inputs(6, 2);
            INST(12, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(11);
            INST(13, Opcode::Return).any().Inputs(12);
        }
    }
    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CompareAnyTypeAfterAnyTypeCheck)
{
    auto graph = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();

        BASIC_BLOCK(2, -1)
        {
            INST(1, Opcode::SaveState).Inputs(0).SrcVregs({0});
            INST(2, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 1);

            INST(3, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE).Inputs(0);
            INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(3);

            INST(5, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_NULL_TYPE).Inputs(0);
            INST(6, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(5);

            INST(7, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0);
            INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(7);

            INST(9, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(10, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(9);

            INST(11, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0);
            INST(12, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(11);

            INST(13, Opcode::SaveState).Inputs(0, 3, 5, 7, 9, 11).SrcVregs({0, 1, 2, 3, 4, 5});
            INST(14, Opcode::CallDynamic).any().InputsAutoType(4, 6, 8, 10, 12, 13);
            INST(15, Opcode::Return).any().Inputs(14);
        }
    }

    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        PARAMETER(0, 0).any();
        CONSTANT(16, 0);
        CONSTANT(17, 1);

        BASIC_BLOCK(2, -1)
        {
            INST(1, Opcode::SaveState).Inputs(0).SrcVregs({0});
            INST(2, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 1);

            INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(16);
            INST(6, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(16);
            INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(16);
            INST(10, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(16);
            INST(12, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(17);

            INST(13, Opcode::SaveState).Inputs(0, 16, 16, 16, 16, 17).SrcVregs({0, 1, 2, 3, 4, 5});
            INST(14, Opcode::CallDynamic).any().InputsAutoType(4, 6, 8, 10, 12, 13);
            INST(15, Opcode::Return).any().Inputs(14);
        }
    }

    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, CompareAnyTypeAfterAnyTypeCheckSpecial)
{
    auto graph = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();

        BASIC_BLOCK(2, -1)
        {
            INST(1, Opcode::SaveState).Inputs(0).SrcVregs({0});
            INST(2, Opcode::AnyTypeCheck)
                .any()
                .AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE)
                .AllowedInputType(profiling::AnyInputType::SPECIAL)
                .Inputs(0, 1);

            INST(3, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE).Inputs(0);
            INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(3);

            INST(5, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_NULL_TYPE).Inputs(0);
            INST(6, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(5);

            INST(7, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0);
            INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(7);

            INST(9, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(10, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(9);

            INST(11, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0);
            INST(12, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(11);

            INST(13, Opcode::SaveState).Inputs(0, 3, 5, 7, 9, 11).SrcVregs({0, 1, 2, 3, 4, 5});
            INST(14, Opcode::CallDynamic).any().InputsAutoType(4, 6, 8, 10, 12, 13);
            INST(15, Opcode::Return).any().Inputs(14);
        }
    }

    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();

    auto graphOpt = CreateGraphDynStubWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        PARAMETER(0, 0).any();
        CONSTANT(16, 0);

        BASIC_BLOCK(2, -1)
        {
            INST(1, Opcode::SaveState).Inputs(0).SrcVregs({0});
            INST(2, Opcode::AnyTypeCheck)
                .any()
                .AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE)
                .AllowedInputType(profiling::AnyInputType::SPECIAL)
                .Inputs(0, 1);

            INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(16);

            INST(5, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_NULL_TYPE).Inputs(0);
            INST(6, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(5);

            INST(7, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0);
            INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(7);

            INST(9, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(10, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(9);

            INST(12, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(16);

            INST(13, Opcode::SaveState).Inputs(0, 16, 5, 7, 9, 16).SrcVregs({0, 1, 2, 3, 4, 5});
            INST(14, Opcode::CallDynamic).any().InputsAutoType(4, 6, 8, 10, 12, 13);
            INST(15, Opcode::Return).any().Inputs(14);
        }
    }
    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}

TEST_F(PeepholesTest, SubOverflowCheckDynCast)
{
    auto graph = CreateGraphDynWithDefaultRuntime();
    GRAPH(graph)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();

        BASIC_BLOCK(2, -1)
        {
            INST(3, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1});
            INST(4, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(5, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(6, Opcode::AddOverflowCheck).i32().Inputs(4, 5, 3);
            INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(6);
            INST(8, Opcode::CastAnyTypeValue)
                .f64()
                .AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE)
                .AllowedInputType(profiling::AnyInputType::INTEGER)
                .Inputs(7);
            INST(9, Opcode::Cast).i32().Inputs(8);
            INST(10, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(9);
            INST(11, Opcode::Return).any().Inputs(10);
        }
    }
    ASSERT_TRUE(graph->RunPass<Peepholes>());
    ASSERT_TRUE(graph->RunPass<Cleanup>());
    GraphChecker(graph).Check();
    auto graphOpt = CreateGraphDynWithDefaultRuntime();
    GRAPH(graphOpt)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();

        BASIC_BLOCK(2, -1)
        {
            INST(4, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0);
            INST(5, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1);
            INST(6, Opcode::Add).i32().Inputs(4, 5);
            INST(10, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(6);
            INST(11, Opcode::Return).any().Inputs(10);
        }
    }
    EXPECT_TRUE(GraphComparator().Compare(graph, graphOpt));
}
// NOLINTEND(readability-magic-numbers)

}  // namespace ark::compiler
